package com.spark.sniffer.plugin;

import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import com.spark.sniffer.logging.api.Logger;
import com.spark.sniffer.logging.api.LoggerFactory;
import com.spark.sniffer.plugin.loader.AgentClassLoader;

public class PluginBootstrap {
    private static final Logger LOGGER = LoggerFactory.getLogger(PluginBootstrap.class);

    public List<AbstractClassEnhancePluginDefine> loadPlugins() throws Exception {
        AgentClassLoader.initDefaultLoader();

        PluginResourcesResolver resolver = new PluginResourcesResolver();
        List<URL> resources = resolver.getResources();

        if (resources == null || resources.size() == 0) {
            LOGGER.info("no plugin files (sniffer-plugin.def) found, continue to start application.");
            return this.loadAllPlugins();
        }

        for (URL pluginUrl : resources) {
            try {
                PluginCfg.INSTANCE.load(pluginUrl.openStream());
            } catch (Throwable t) {
                LOGGER.error(t, "plugin file [{}] init failure.", pluginUrl);
            }
        }

        List<PluginDefine> pluginClassList = PluginCfg.INSTANCE.getPluginClassList();

        List<AbstractClassEnhancePluginDefine> plugins = new ArrayList<AbstractClassEnhancePluginDefine>();
        for (PluginDefine pluginDefine : pluginClassList) {
            try {
                LOGGER.debug("loading plugin class {}.", pluginDefine.getDefineClass());
                Class<?> pluginDefineClass = Class.forName(pluginDefine.getDefineClass(), true, AgentClassLoader
                    .getDefault());
                if(pluginDefineClass.getAnnotation(Deprecated.class) == null) {
                    plugins.add((AbstractClassEnhancePluginDefine) pluginDefineClass.newInstance());
                } else {
                    LOGGER.debug("{} 已废弃", pluginDefine.getDefineClass());
                }
            } catch (Throwable t) {
                LOGGER.error(t, "load plugin [{}] failure.", pluginDefine.getDefineClass());
            }
        }
        plugins.addAll(DynamicPluginLoader.INSTANCE.load(AgentClassLoader.getDefault()));
        LOGGER.info("loading plugin size {}", plugins.size());
        return plugins;
    }

    public List<AbstractClassEnhancePluginDefine> loadAllPlugins() throws Exception {
        List<AbstractClassEnhancePluginDefine> plugins = new ArrayList<AbstractClassEnhancePluginDefine>();

        String basePackage = (PluginBootstrap.class.getPackage().getName() + ".plugins.define").replace('.', '/');
        Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(basePackage);

        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();

            if ("jar".equals(resource.getProtocol())) {
                try {
                    JarFile jar = ((JarURLConnection)resource.openConnection()).getJarFile();
                    Enumeration<JarEntry> entries = jar.entries();
                    while (entries.hasMoreElements()) {
                        JarEntry entry = entries.nextElement();
                        String name = entry.getName(); // com/spark/sniffer/plugin/plugins/RestControllerPlugin.class

                        if (!name.startsWith(basePackage) || !name.endsWith(".class") || name.contains("$")) {
                            continue;
                        }
                        String className = name.replace("/", ".").substring(0, name.length() - 6);
                        Class<?> pluginDefineClass = Class.forName(className);
                        if(pluginDefineClass.getAnnotation(Deprecated.class) == null) {
                            plugins.add((AbstractClassEnhancePluginDefine) pluginDefineClass.newInstance());
                        } else {
                            LOGGER.debug("{} 已废弃", className);
                        }
                    }

                } catch (IOException e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
        }
        LOGGER.info("loading plugin size {}", plugins.size());
        return plugins;
    }

}
